/*
 *
 *  * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 *
 *
 */

package com.iwindplus.base.util;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.AES;
import com.iwindplus.base.domain.constant.CommonConstant;
import com.iwindplus.base.domain.enums.AlgorithmTypeEnum;
import com.iwindplus.base.domain.enums.BizCodeEnum;
import com.iwindplus.base.domain.exception.BizException;
import com.iwindplus.base.util.domain.constant.UtilConstant.CryptoConstant;
import com.iwindplus.base.util.domain.dto.CryptoDTO;
import java.util.HashMap;
import java.util.Map;

/**
 * 加解密安全相关工具类.
 *
 * @author zengdegui
 * @since 2025/04/19 00:40
 */
public class CryptoUtil {

    private CryptoUtil() {
        throw new IllegalStateException(CommonConstant.UTILITY_CLASS);
    }

    /**
     * 加密（聚合）.
     *
     * @param data   待加密数据
     * @param config 加解密配置
     * @return String
     */
    public static String encrypt(String data, CryptoDTO config) {
        if (Boolean.FALSE.equals(config.getEnabled())) {
            return data;
        }

        final AlgorithmTypeEnum algorithm = config.getAlgorithm();
        if (ObjectUtil.isEmpty(algorithm)) {
            throw new BizException(BizCodeEnum.CRYPTO_ALGORITHM_NOT_SUPPORT);
        }
        switch (algorithm) {
            case BASE64:
                return CryptoUtil.encryptByBase64(data);
            case AES:
                return CryptoUtil.encryptByAes(data, config.getKey(), config.getIv());
            case RSA:
                return CryptoUtil.encryptByRsa(data, config.getPublicKey());
            case SM2:
                return CryptoUtil.encryptBySm2(data, config.getPublicKey());
            case SM4:
                return CryptoUtil.encryptBySm4(data, config.getKey());
            default:
                return null;
        }
    }

    /**
     * 解密（聚合）.
     *
     * @param data   待解密数据
     * @param config 加解密配置
     * @return String
     */
    public static String decrypt(String data, CryptoDTO config) {
        if (Boolean.FALSE.equals(config.getEnabled())) {
            return data;
        }

        final AlgorithmTypeEnum algorithm = config.getAlgorithm();
        if (ObjectUtil.isEmpty(algorithm)) {
            throw new BizException(BizCodeEnum.CRYPTO_ALGORITHM_NOT_SUPPORT);
        }
        switch (algorithm) {
            case BASE64:
                return CryptoUtil.decryptByBase64(data);
            case AES:
                return CryptoUtil.decryptByAes(data, config.getKey(), config.getIv());
            case RSA:
                return CryptoUtil.decryptByRsa(data, config.getPrivateKey());
            case SM2:
                return CryptoUtil.decryptBySm2(data, config.getPrivateKey());
            case SM4:
                return CryptoUtil.decryptBySm4(data, config.getKey());
            default:
                return null;
        }
    }

    /**
     * Base64加密.
     *
     * @param data 待加密数据
     * @return String
     */
    public static String encryptByBase64(String data) {
        return Base64.encode(data);
    }

    /**
     * Base64解密.
     *
     * @param data 待解密数据
     * @return String
     */
    public static String decryptByBase64(String data) {
        return Base64.decodeStr(data);
    }

    /**
     * AES加密.
     *
     * @param data 待加密数据
     * @param key  密钥（32位）
     * @param iv   初始化向量（12位）
     * @return String
     */
    public static String encryptByAes(String data, String key, String iv) {
        final AES aes = CryptoUtil.setAes(key, iv);
        return aes.encryptBase64(data);
    }

    /**
     * AES解密.
     *
     * @param data 待解密数据
     * @param key  密钥（32位）
     * @param iv   初始化向量（12位）
     * @return String
     */
    public static String decryptByAes(String data, String key, String iv) {
        final AES aes = CryptoUtil.setAes(key, iv);
        return aes.decryptStr(data);
    }

    /**
     * sm4加密.
     *
     * @param data 待加密数据
     * @param key  密钥（16位）
     * @return String
     */
    public static String encryptBySm4(String data, String key) {
        if (key.length() != CryptoConstant.SM4_KEY_LENGTH) {
            throw new BizException(BizCodeEnum.INVALID_SM4_KEY);
        }
        return SmUtil.sm4(key.getBytes()).encryptBase64(data);
    }

    /**
     * sm4解密.
     *
     * @param data 待解密数据
     * @param key  密钥（16位）
     * @return String
     */
    public static String decryptBySm4(String data, String key) {
        if (key.length() != CryptoConstant.SM4_KEY_LENGTH) {
            throw new BizException(BizCodeEnum.INVALID_SM4_KEY);
        }
        return SmUtil.sm4(key.getBytes()).decryptStr(data);
    }

    /**
     * 产生sm2加解密需要的公钥和私钥.
     *
     * @return Map<String, String>
     */
    public static Map<String, String> generateSm2Key() {
        Map<String, String> keyMap = new HashMap<>(16);
        SM2 sm2 = SmUtil.sm2();
        keyMap.put(CryptoConstant.PRIVATE_KEY, sm2.getPrivateKeyBase64());
        keyMap.put(CryptoConstant.PUBLIC_KEY, sm2.getPublicKeyBase64());
        return keyMap;
    }

    /**
     * sm2公钥加密.
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     * @return String
     */
    public static String encryptBySm2(String data, String publicKey) {
        SM2 sm2 = SmUtil.sm2(null, publicKey);
        return sm2.encryptBase64(data, KeyType.PublicKey);
    }

    /**
     * sm2私钥解密
     *
     * @param data       待解密数据
     * @param privateKey 私钥
     * @return String
     */
    public static String decryptBySm2(String data, String privateKey) {
        SM2 sm2 = SmUtil.sm2(privateKey, null);
        return sm2.decryptStr(data, KeyType.PrivateKey);
    }

    /**
     * 产生RSA加解密需要的公钥和私钥.
     *
     * @return Map<String, String>
     */
    public static Map<String, String> generateRsaKey() {
        Map<String, String> keyMap = new HashMap<>(16);
        RSA rsa = SecureUtil.rsa();
        keyMap.put(CryptoConstant.PRIVATE_KEY, rsa.getPrivateKeyBase64());
        keyMap.put(CryptoConstant.PUBLIC_KEY, rsa.getPublicKeyBase64());
        return keyMap;
    }

    /**
     * rsa公钥加密.
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     * @return String
     */
    public static String encryptByRsa(String data, String publicKey) {
        RSA rsa = SecureUtil.rsa(null, publicKey);
        return rsa.encryptBase64(data, KeyType.PublicKey);
    }

    /**
     * rsa私钥解密.
     *
     * @param data       待解密数据
     * @param privateKey 私钥
     * @return String
     */
    public static String decryptByRsa(String data, String privateKey) {
        RSA rsa = SecureUtil.rsa(privateKey, null);
        return rsa.decryptStr(data, KeyType.PrivateKey);
    }

    /**
     * md5加密.
     *
     * @param data 待加密数据
     * @return String
     */
    public static String encryptByMd5(String data) {
        return SecureUtil.md5(data);
    }

    /**
     * sha256加密.
     *
     * @param data 待加密数据
     * @return String
     */
    public static String encryptBySha256(String data) {
        return SecureUtil.sha256(data);
    }

    /**
     * sm3加密.
     *
     * @param data 待加密数据
     * @return String
     */
    public static String encryptBySm3(String data) {
        return SmUtil.sm3(data);
    }

    private static AES setAes(String key, String iv) {
        if (key.length() != CryptoConstant.AES_KEY_LENGTH) {
            throw new BizException(BizCodeEnum.INVALID_AES_KEY);
        }
        if (iv.length() != CryptoConstant.AES_IV_LENGTH) {
            throw new BizException(BizCodeEnum.INVALID_AES_IV);
        }
        return new AES(CryptoConstant.AES_MODE, CryptoConstant.AES_PADDING, key.getBytes(), iv.getBytes());
    }

}
